package org.diy.traffic.sinks;

import java.util.Date;
import java.util.HashMap;
import java.util.logging.Level;

import org.diy.traffic.sinks.TwitterStatusSink.TwitterStatusSinkException;
import org.diy.traffic.updates.TrafficUpdate;
import org.diy.traffic.updates.TrafficUpdate.UnknownDataException;
import org.diy.traffic.util.TLog;

import winterwell.jtwitter.TwitterException;
import winterwell.jtwitter.TwitterRateLimitException;

/**
 * This class represents a TrafficSink which is capable of handling updates originating from
 * a TwitterDMSource. These updates contain a DM_RECIPIENT field in the data HashMap, which contains
 * the name of the Twitter user to which the update should be sent via Direct Message
 * @author Dan Greenblatt
 *
 */
public class TwitterDMSink extends TwitterSink {

	private static String sinkType = "TWITTER_DM_SINK";
	
	private static HashMap<String, TwitterDMSink> myInstances = 
		new HashMap<String, TwitterDMSink>();
	
	public static TwitterDMSink getInstanceByUsername(String username) {
		return myInstances.get(sinkType + "_" + username);
	}
	
	public static TwitterDMSink createInstance(String username, String password) {
		return TwitterDMSink.createInstance(username, password, TwitterSink.DEFAULT_RATE_LIMIT);
	}
	public static TwitterDMSink createInstance(String username, String password, int rateLimit) {
		TwitterDMSink tds = new TwitterDMSink(username, password, rateLimit);
		myInstances.put(tds.getName(), tds);
		return tds;
	}
	
	public TwitterDMSink(String username, String password) {
		this(username, password, TwitterSink.DEFAULT_RATE_LIMIT);
	}
	
	public TwitterDMSink(String username, String password, int rateLimit) {
		super(username,password, rateLimit);
		this.sinkName = TwitterDMSink.sinkType + "_" + username;
	}
	/**
	 * Performs the bulk of the work of sending the content of a TrafficUpdate via Direct Message
	 * to a Twitter user.
	 * @param t the TrafficUPdate to process
	 * @return true if the update is properly handled, false if it's not handled because handling it would put
	 * 	us over the rate limit
	 * TODO this function shouldn't have to care about rate limits. This should be checked BEFORE forwarding
	 * 	the update on to this object. This will be cleaner, and more in keeping with the InputDaemon / Source
	 * 	side of the equation!
	 */
	@Override
	public boolean processUpdate(TrafficUpdate t) throws TrafficSinkException {
		if (this.inChillaxMode())
			return false;
		
		// check to see if you're within the allowable rate limit
		if (isWithinRateLimit()) {
			try {
				// make sure you cut the message down to 140 chars if it's not already
				String twitterized_msg = t.getMessage();
				if (twitterized_msg.length() > 140) {
					twitterized_msg = twitterized_msg.substring(0, 140);
				}
				
				// send a message to the recipient specified by the DM_RECIPIENT field in the TrafficUpdate's data object
				myTwitter.sendMessage((String)t.getData(TwitterSink.DM_RECIPIENT), twitterized_msg);
			} catch (UnknownDataException ude) {
				throw new TrafficSinkException("Can't send message, don't understand update: " + ude.getMessage());
			} catch (TwitterException te) {
				if (te.getClass() == TwitterRateLimitException.class) {
					// wait for ten minutes before trying again ....
					long chillaxUntil = System.currentTimeMillis() + 600000;
					TLog.log(Level.INFO, String.format("[%s] laying low until %s", this.sinkName, new Date(chillaxUntil)));
					setChillaxUntil(chillaxUntil);
					throw new TrafficSinkException("Ran up agains rate limit: " + te.getMessage());
				} else 
					throw new TrafficSinkException("Can't send message, Twitter problem: " + te.getMessage());
			} 
			//TLog.log(Level.FINEST, "****is ok to update here");
			// perform any accounting necessary now that you've actually handled this update
			didHandleUpdate();
			return true;
		} else {
			TLog.log(Level.FINEST, "[" + this.sinkName+ "]" + "outside of allowable rate limit, not handling update.");
			return false;
		}
	}
}
